/**
 * 
 */
package com.taobao.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeanUtils;

import com.taobao.api.TaobaoApiException;
import com.taobao.api.extra.model.TradeAddRequest;
import com.taobao.api.extra.model.TradeAddResponse;
import com.taobao.api.model.DeliverySendRequest;
import com.taobao.api.model.DeliverySendResponse;
import com.taobao.api.model.Item;
import com.taobao.api.model.ItemsGetRequest;
import com.taobao.api.model.ItemsGetResponse;
import com.taobao.api.model.Order;
import com.taobao.api.model.Refund;
import com.taobao.api.model.RefundGetRequest;
import com.taobao.api.model.RefundGetResponse;
import com.taobao.api.model.RefundsApplyGetRequest;
import com.taobao.api.model.RefundsApplyGetResponse;
import com.taobao.api.model.RefundsRecieveGetRequest;
import com.taobao.api.model.RefundsRecieveGetResponse;
import com.taobao.api.model.Sku;
import com.taobao.api.model.TaobaoResponse;
import com.taobao.api.model.Trade;

import com.taobao.api.model.TradesBoughtGetRequest;
import com.taobao.api.model.TradesGetResponse;
import com.taobao.api.model.TradesSoldGetRequest;
import com.taobao.client.obj.TopItem;
import com.taobao.client.obj.TopOrder;
import com.taobao.client.obj.TopRefund;
import com.taobao.client.obj.TopSku;
import com.taobao.client.obj.TopTrade;
import com.taobao.service.SessionService;
import com.taobao.service.TradeService;


/**
 * Implementation of the service which handles different kinds of request about trades
 * 
 * @author hujingfei.pt@taobao.com
 *
 */
public class TradeServiceImpl implements TradeService{
	
	private SessionService sessionService;
	private final static String TRADE_TYPE = "shopex_trade";
		
	/**
	 * Constructor.
	 * 
	 * @param client a client object to set
	 * @param sessionService a SessionService object to set
	 */
	public TradeServiceImpl(SessionService sessionService) {
		this.sessionService = sessionService;
	}

	/**
	 * Get a list of trades as a seller
	 * 
	 * @param sessionid the session id(key)
	 * @param nick the nickname of the seller
	 * @return a list of trades
	 * @throws Exception when some services behind back-end is unavailable, throw exceptions
	 */
	public List<TopTrade> tradeSoldGet(String nick) throws Exception {
		String fields = 
			"pic_path,price,tid,seller_nick,buyer_nick,title,type,created,sid,tid,seller_rate,buyer_rate,status,payment,orders,consign_time," +
			"total_fee,discount_fee,adjust_fee,discount_fee,adjust_fee,post_fee,sid,out_sid,pay_time,end_time,modified,buyer_obtain_point_fee," +
			"point_fee,real_point_fee";
		
		TradesSoldGetRequest tradesSoldGetRequest = new TradesSoldGetRequest();
		tradesSoldGetRequest.setFields(fields);
		tradesSoldGetRequest.setType(TRADE_TYPE);

		TradesGetResponse tradesGetResponse = sessionService.getClient().tradesSoldGet(tradesSoldGetRequest, sessionService.getSession(), nick);

		processError(tradesGetResponse);

		List<Trade> trades = tradesGetResponse.getTrades();

		return fetchTopTrade(trades);
	}	

	/**
	 * Get a list of trades as a buyer
	 * 
	 * @param nick the nickname of the buyer
	 * @return a list of trades
	 * @throws Exception when some services behind back-end is unavailable, throw exceptions
	 */
	public List<TopTrade> tradeBoughtGet(String nick) throws Exception {
		
		// Obtain an encapsulated request object of getting bought trades
		TradesBoughtGetRequest tradesRequest = new TradesBoughtGetRequest();
		// Fields which will have value in the resulting trade object
		String fields = "seller_nick,buyer_nick,title,type,created,sid,tid,seller_rate,"
				+ "buyer_rate,status,payment,discount_fee,adjust_fee,post_fee,pay_time,"
				+ "end_time,consign_time,modified,buyer_obtain_point_fee,point_fee,real_point_fee,"
				+ "received_payment,pic_path,iid,num,price,cod_fee,shipping_type,alipay_no,orders";
		tradesRequest.setFields(fields);		

		final int pageSize = 100;
		tradesRequest.setPageSize(pageSize);
		tradesRequest.setType(TRADE_TYPE);
		
		try {
			// Return a response object through which data can be fetched
			TradesGetResponse tradesBoughtGet = sessionService.getClient().tradesBoughtGet(tradesRequest, sessionService.getSession(), 
					sessionService.getLoginName());
			// Handle errors such as service unavailable and invalid session and so on
			processError(tradesBoughtGet);

			List<Trade> tradeList = tradesBoughtGet.getTrades();

			if (tradeList != null) {
				int page = 1;
				while (tradeList.size() < tradesBoughtGet.getTotalResults()) {
					tradesRequest.setPageNo(page++);
					tradesBoughtGet = sessionService.getClient().tradesBoughtGet(tradesRequest, sessionService
							.getSession(), sessionService.getLoginName());
					tradeList.addAll(tradesBoughtGet.getTrades());
				}
			}

			// A new TopTrade list
			List<TopTrade> topTradeList = fetchTopTrade(tradeList);
			
			
			return topTradeList;
		} catch (TaobaoApiException e) {
			e.printStackTrace();
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}		
	}

	/** 
	 * Create a trade between the buyer and the seller
	 * 
	 * @param item the item covered in the trade
	 * @param buyer the nickname of the buyer
	 * @param seller the nickname of the seller
	 * @throws Exception when some services behind back-end is unavailable, throw exceptions
	 */
	public void addTrade(List<TopItem> items, String buyer, String seller,
			String name, String phone, String mobile, String province,
			String city, String area, String division_code, String post_code,
			String address_detail, boolean isNewAddress, String memos,String discount)
			throws Exception {
		try {

			TradeAddRequest request = new TradeAddRequest();


			String skuIds = "";
			String titles = "";
			String iids = "";
			String prices = "";
			String nums = "";
			// String memos = "";

			for (TopItem i : items) {
				titles += i.getTitle() + ",";
				iids += i.getIid() + ",";
				prices += i.getPrice() + ",";
				nums += "1,";
				// memos += "SHOPEX MEMO,";
			}

			request.setSaveDeliverAddr(isNewAddress);
			request.setDiscountFees(discount
					.substring(0, discount.length() - 1));
			request.setTitles(titles.substring(0, titles.length() - 1));
			request.setIids(iids.substring(0, iids.length() - 1));
			request.setPrices(prices.substring(0, prices.length() - 1));

			if (skuIds.length() > 0)
				request.setSkuIds(skuIds.substring(0, skuIds.length() - 1));
			else
				request.setSkuIds("");

			request.setNums(nums.substring(0, nums.length() - 1));
			request.setSellerNick(seller);
			request.setLocationZip("123456");
			request.setPostFee("123.12");
			request.setDivisionCode(division_code);
			request.setLocationAddress(address_detail);
			request.setSnapshot("jeck snapshot1");
			Map<String, String> orderSnap = new HashMap<String, String>();

			for (TopItem i : items) {
				String sku = "";
				if (i.getTopSku() != null && i.getTopSku().size() > 0)
					sku += i.getTopSku().get(0).getSkuId();

				orderSnap
						.put("snapshot_" + i.getIid() + "_" + sku, "jeck snapshot2");
			}

			request.setOrderSnapshots(orderSnap);
			request.setLogisticType("express");

			request.setType(TRADE_TYPE);
			request.setSnapshot("snapshot");

			request.setPostFee("5");
			request.setDivisionCode(division_code);
			request.setLocationZip("123456");

			request.setLocationAddress(address_detail);
			request.setLocationCity(city);
			request.setLocationState(province);
			request.setLocationDistrict(area);
			request.setReceiverName(name);
			request.setPhone(phone);
			request.setMobile(mobile);
			request.setOrderMemos(memos.substring(0, memos.length() - 1));
			try {
				TradeAddResponse tradeAddResponse = sessionService.getClient().tradeAdd(request, sessionService
						.getSession(), sessionService.getLoginName());
				processError(tradeAddResponse);
			} catch (TaobaoApiException e) {
				throw new Exception(e.getMessage());
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 
	 * @param tid Id of the trade
	 * @throws Exception
	 */
	public void deliverySend(String tid) throws Exception{
		DeliverySendRequest r = new DeliverySendRequest();
		r.setTid(tid);
		r.setOrderType("virtual_goods");
		DeliverySendResponse deliverySend = sessionService.getClient().deliverySend(r, sessionService.getSession(),sessionService.getLoginName());
		processError(deliverySend);
	}
	
	/**
	 * Get the items of the seller
	 * 
	 * @param nick the nickname of the seller
	 * @return a list of items
	 * @throws Exception when some services behind back-end is unavailable, throw exceptions
	 */
	public List<TopItem> itemsGet(String nick) throws Exception {
		final String ITEMS_GET_FIELDS_STRING = "iid,title,nick,type,cid,pic_path,delist_time,price,post_fee,sku";
		ItemsGetRequest itemGetRequest = new ItemsGetRequest();
		itemGetRequest.setFields(ITEMS_GET_FIELDS_STRING);
		itemGetRequest.setNicks(nick);
		try {
			ItemsGetResponse itemsGet = sessionService.getClient().itemsGet(itemGetRequest);
			
			processError(itemsGet);

			List<Item> items = itemsGet.getItems();
			List<TopItem> top = new ArrayList<TopItem>();

			if (items == null || items.size() < 0)
				return top;

			for (Item i : items) {
				TopItem t = new TopItem();
				List<Sku> skus = i.getSkus();
				List<TopSku> ks = new ArrayList<TopSku>();

				if (skus != null) {
					for (Sku s : skus) {
						TopSku sk = new TopSku();
						sk.setSkuId(s.getSkuId());
						sk.setPrice(s.getPrice());
						sk.setQuantity(s.getQuantity());
						ks.add(sk);
					}
				}				
				t.setIid(i.getIid());
				t.setPic(i.getPicPath());
				t.setPrice(i.getPrice());
				t.setTitle(i.getTitle());
				t.setTopSku(ks);
				top.add(t);
			}
			return top;
		} catch (TaobaoApiException e) {
			throw new Exception(e.getMessage());
		}
	}
	
	
	/**
	 * Get the refunds the buyer applied
	 * 
	 * @param nick the nickname of the buyer
	 * @return a list of refunds
	 * @throws Exception when some services behind back-end is unavailable, throw exceptions
	 */
	public List<TopRefund> getBuyerRefunds(String nick)	throws Exception {
		// A request object.
		RefundsApplyGetRequest req = new RefundsApplyGetRequest();
		// Set fields.
		req.setFields("refund_id,tid,title,buyer_nick,seller_nick,total_fee,status,created,refund_fee");
		// Must be independent_shop_trade.
		req.setType(TRADE_TYPE);
		// Return a response object.
		RefundsApplyGetResponse rsp = sessionService.getClient().refundsApplyGet(req, sessionService.getSession(), 
				sessionService.getLoginName());
		// Handle various errors.
		processError(rsp);
		// Create a list of top refunds.
		List<TopRefund> topRefundList = new ArrayList<TopRefund>();
		// A reference to a list of refunds.
		List<Refund> refundList = rsp.getRefundList();
		// Copy data from refundList to topRefundList.
		if (refundList != null) {
			// Iterate refundList
			for (Refund refund : refundList) {
				// New a top refund object.
				TopRefund topRefund = new TopRefund();
				BeanUtils.copyProperties(refund, topRefund);
				topRefundList.add(topRefund);
			}
		}
		
		return topRefundList;	 
	}
	
	/**
	 * Get the refunds the seller received
	 * 
	 * @param seller the nickname of the seller
	 * @return a list of refunds
	 * @throws Exception when some services behind back-end is unavailable, throw exceptions
	 */
	public List<TopRefund> getSellerRefund(String seller) throws Exception {
		// New a request object
		RefundsRecieveGetRequest req = new RefundsRecieveGetRequest();
		// Set fields
		req.setFields("refund_id,tid,seller_nick,order_status,status,good_status");
		
		// Return a response object
		RefundsRecieveGetResponse rsp = sessionService.getClient().refundsRecieveGet(req, sessionService.getSession(), sessionService.getLoginName());
		// Handle various errors
		processError(rsp);
		// Create a list of top refunds
		List<TopRefund> topRefundList = new ArrayList<TopRefund>();
		// A reference to a list of refunds
		List<Refund> refundList = rsp.getRefundList();
		// Copy data from refundList to topRefundList
		if (refundList != null) {
			// Iterate refundList
			for (Refund refund : refundList) {
				// New a top refund object
				TopRefund topRefund = new TopRefund();
				BeanUtils.copyProperties(refund, topRefund);
				topRefundList.add(topRefund);
			}
		}
		return topRefundList;	 
	}
	
	/**
	 * Get the concrete information of the refund
	 * 
	 * @param refundId the id of the refund.
	 * @return a refund object.
	 * @throws Exception when some services behind back-end is unavailable, throw exceptions
	 */
	public TopRefund getRefundInfo(String refundId) throws Exception {
		RefundGetRequest req = new RefundGetRequest();
		req.setRefundId(refundId);
		req.setFields("refund_id,alipay_no,tid,oid,buyer_nick,seller_nick,total_fee,status,created," +
				"refund_fee,good_status,has_good_return,payment,reason,desc,iid,title,price,num," +
				"good_return_time,company_name,sid,address ");

		RefundGetResponse rsp = sessionService.getClient().refundGet(req, sessionService.getSession(), sessionService.getLoginName());

		processError(rsp);
		
		if (rsp != null) {
			TopRefund tr = new TopRefund();
			BeanUtils.copyProperties(rsp.getRefund(), tr);
			return tr;
		}
		return null;
	}
	
	
	/**
	 * 
	 * @param r a response object
	 * @throws Exception When some service is unavailable, throw exception
	 */
	private void processError(TaobaoResponse r) throws Exception {
		if (!r.isSuccess()) {
			throw new Exception(r.getErrorCode() + ":" + r.getMsg());
		}
	}
	
	/**
	 * Return a list of top trades from a list of trades
	 * 
	 * @param trades Trade list to be transfered 
	 * @return A list of top trades
	 */
	private List<TopTrade> fetchTopTrade(List<Trade> trades) {
		List<TopTrade> trs = new ArrayList<TopTrade>();
		if (trades != null) {
			for (Trade t : trades) {
				TopTrade tr = new TopTrade();
				try {
					List<Order> orders = t.getOrders();
					List<TopOrder> tos = new ArrayList<TopOrder>();
					for (Order o : orders) {
						TopOrder or = new TopOrder();
						BeanUtils.copyProperties(o, or);
						tos.add(or);
					}
					BeanUtils.copyProperties(t, tr);
					tr.setTopOrders(tos);
					trs.add(tr);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return trs;
	}
}
